home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Arsenal Files 8
/
The Arsenal Files Collection #8 (Arsenal Computer) (1996).ISO
/
prg_casm
/
miracle.zip
/
MIRACLE.TXT
< prev
next >
Wrap
Text File
|
1996-10-02
|
61KB
|
2,686 lines
User Manual
Miracle C Compiler
version 1.6
3 October 1996
Copyright 1989-96 bts
The Miracle C Compiler runs on a 386 PC (or better) under MS-
DOS, accepting a dialect of the C language and generating
object code suitable for Microsoft or compatible linker.
All of traditional (Kernighan & Ritchie) C syntax is
implemented, including record (struct/union) and enumerated
data types, int, long and floating point data types, user type
definition, bit fields in structs, initializers for all data
types. Both traditional and new (ANSI) function declaration is
supported. There is a comprehensive library of functions.
Table of Contents
REGISTRATION 4
DISCLAIMER 4
USING THE COMPILER 4
FLOATING POINT SUPPORT 5
ENVIRONMENT VARIABLES 5
COMPILER SWITCHES 5
EXAMPLE PROGRAMS 6
LANGUAGE FEATURES 7
LEXICAL ELEMENTS 7
1. Operators 7
2. Separators 7
3. Identifiers 7
4. Reserved words 7
5. Constants 7
PREPROCESSOR 8
Macro substitution 8
File inclusion 9
Conditional Compilation 10
Other Features 11
FUNCTIONS 11
TYPES 13
Record (Struct/Union) 14
Typedef 14
Type Compatibility 15
DECLARATIONS 15
Initializers 18
EXPRESSIONS 19
STATEMENTS 21
ASSEMBLER PROGRAMMING INTERFACE 22
FUNCTION LIBRARY 23
STARTUP CODE 23
INCLUDE HEADER FILES 23
LIBRARY FUNCTIONS 23
abs, labs 23
calloc 23
close 24
create 25
exit 25
fclose 25
feof 26
fflush 26
fgetc 27
fgets 27
fopen 28
fprintf 28
fputc 29
fputs 29
fread 30
free 30
fscanf 31
fwrite 31
getc 31
getchar 32
gets 32
is-ctype 33
malloc 33
memchr 34
memcmp 34
memcpy, memmove 34
memset 34
open 35
printf 35
putc 36
putchar 36
puts 37
read 37
scanf 38
sprintf 39
sscanf 39
strcat 39
strchr 40
strcmp 40
strcpy 40
strdup 40
strlen 40
strlwr 41
strncat 41
strncmp 41
strncpy 41
strnicmp 41
strpbrk 42
strrchr 42
strrev 42
strspn 42
strupr 42
tolower, toupper 43
ungetc 43
vsprintf 43
write 44
REGISTRATION
The compiler is shareware, and is supplied on a trial use
basis. The compiler package is copyright material and should
not be modified or disassembled. Programs written using the
compiler may be freely distributed.
If you find it useful you should register. Registration costs
US$15, Can$20 or GBP10, and entitles you to receive upgrades
for one year. Payment should be made out to ôMiracle Software
Technologyö.
Registration is from the developer;
Miracle Software Technology
PO Box 52051
41 York Street
Ottawa, Ontario K1N 5S0
Canada
At the time of writing Miracle C is under active development
by the author, and a further release is planned for winter
1996/7. By registering you support further development of the
compiler. Should you wish to make a direct contribution to a
future version (for example by writing a compiler utility,
library functions or source code), please write to the
developer at the above address.
DISCLAIMER
The compiler should be regarded as a teaching tool and is
distributed on the shareware principle, without any warranty,
without even warranty of merchantability or fitness for a
particular purpose. No guarantee is made regarding the
performance of the compiler, and no responsibility can be
taken for use of, or inability to use, the compiler.
It is intended that the next release of Miracle C will have
compiler source code available on registration, in support of
the compilerÆs intended role as a teaching support tool.
USING THE COMPILER
The following files are present in a compressed form in the
archive;
miracle.doc documentation in MS Word format
miracle.txt documentation in text format
cc.exe the C Compiler
ccl.lib C compiler library
mc.bat batch file for compiler
and several example programs;
example.c
hanoi.c
cat.c
sieve.c
maze.c maze
slr.c grammar.c
Floating point support
The compiler requires an 8087 coprocessor (or better), or
emulation program, to perform floating point arithmetic. Such
a coprocessor is integrated into the 80486 and subsequent
CPUs. If your system does not have a numeric coprocessor you
should obtain a public domain emulation program such as
em87.com to perform floating point computations.
Environment Variables
The compiler uses the following environment variables;
LIB directory containing library ccl.lib
(this is only used by a linker)
INCLUDE directory containing system include files <*.h>
(if unset, defaults to \cc\include)
LINKER name of linker, eg LINKER=BLINK
(if unset, defaults to LINK, the Microsoft linker)
Note that a linker is not supplied with the
compiler.
Use the Microsoft linker supplied with DOS, or a
compatible linker.
CC output is Microsoft/Intel object module format,
as documented
in the MS-DOS Encyclopedia. The linker must accept
Microsoft object.
Before compiling a program set the environment variables to
suitable values;
C:\> SET LIB=\CC
C:\> SET INCLUDE=\CC\INCLUDE
C:\> SET LINKER=LINK
Compile one of the example files;
C:\CC> MC EXAMPLE
Microsoft (R) Overlay Linker Version 3.51
Copyright (C) Microsoft Corp 1983, 1984, 1985, 1986. All
rights reserved.
C:\CC>
This will compile and link `example.c', creating example.obj,
example.exe.
Compiler switches
-j signed char constants
-n noisy compile
-p disable autoprototyping
-r set error limit
-s generate source listing (.src) after preprocessing
-t allow trigraphs
-w no warnings
-Dname define macro
-Uname undefine macro
-Iname add include directory
Only one program file may be compiled at one time. To compile
and link together several programs, compile them separately
with `-c' option, then link them with ccl.lib.
If the compiler finds an error (in preprocessing, parsing or
other compilation error, eg an undeclared variable) it will
print the statement where it occurred and stop if the error
limit has been reached. Functions should be declared before
being called, although this is not mandatory. If they are not,
then auto-prototyping will generate a prototype for a new
function returning int. A function definition automatically
declares the function if it has not been declared or auto-
prototyped earlier.
EXAMPLE PROGRAMS
A few example programs are included to illustrate Miracle C
facilities.
example.c hanoi.c
cat.c sieve.c
maze.c maze
slr.c grammar.c
example.c shows most of CC's features; structs/unions;
initializers for structs and arrays;
typedefs; function prototyping; functions with local
variables, and scoping;
data types; signed and unsigned int, char and long
variables, floating point;
use of some library functions for file handling
cat.c reverse words in a sentence, eg `I am here' to
`here am I'
hanoi.c non-recursive towers of hanoi
(originally from a Programmer's Challenge in
Computer Shopper magazine!!)
gives the sequence of moves to move all discs from
first to third peg via second
sieve.c Eratosthenes sieve to find prime numbers by
eliminating composites
developed from a BYTE C benchmark program
maze.c maze solver, finds shortest path through maze
from A to B
maze eg. C:\> MAZE<MAZE
uses an algorithm from graph theory to find shortest
path
slr.c simple parser generator for SLR grammar
given a grammar file, generates a recognizer program
to parse the SLR grammar
grammar grammar file for slr.c
LANGUAGE FEATURES
This is a summary of the C language as implemented by the
Miracle C compiler.
For a tutorial on the C language, read `The C Programming
Language' by Kernighan and Ritchie. A comprehensive reference
to the language is the book `C: A Reference Manual' by
Harbison and Steele, published by Prentice-Hall, which
describes both traditional and the new ANSI C syntax and
semantics. Miracle C implements the traditional C language and
some of the ANSI extensions.
Lexical Elements
Tokens
The fundamental lexical elements of the language are
operators, separators, identifiers, reserved-words and
constants.
A C source program consists of tokens separated by whitespace.
Whitespace is defined as chars 9-13 and 32. Tokens can be
operators, separators, identifiers, reserved-words or
constants.
1. Operators
The following operators are legal in C.
! % ^ & * - + = ~ | < > ' ? += -= *= /= %= <<= >>= &= ^= |=
-> ++ -- << >> <= >= == != && ||
2. Separators
Whitespace characters 9-13, and space character 32 are
separators.
( ) [ ] { } , ; :
3. Identifiers
A sequence of any number of letters/digits/underscores
starting with a letter or underscore character.
Identifiers are case-sensitive so name, Name and NAME are
different, but external symbols (such as function names) are
not case-sensitive.
4. Reserved words
auto break case char default do else enum extern for goto if
int long register return short signed sizeof static struct
switch typedef union unsigned void while
5. Constants
A C constant can be an integer, character or string, or
floating point constant.
integer
An integer can be represented as a decimal, octal (base 8) or
hexadecimal (base 16) constant.
* decimal (digits)
* octal 0(digits), eg 0243
* hex '0x' (or 0X) (hex-digits 0-9 a-f A-F), eg 0x4afb
A numeric constant can be unsigned (suffixed by U, eg 56U),
long (eg 128374L) or floating point.
Floating point constants have syntax aaa.bbbEeee where
aaa.bbb = mantissa
eee = exponent (signed,
optional)
character
A character is 'char' or '\esc-char'
where esc-char= nnn octal literal
xnn hex literal
a alert b backspace
f form feed n newline
t tab r carriage return
v vertical tab ' single quote
? question mark " double quote
A character constant may contain more than one byte, on which
case bytes are packed into a word,
for example int a='xy';
string
A string is "string" where the string has printable characters
or \escaped-characters.
Constant strings are null-terminated, eg sizeof("hello")=6
Preprocessor
The preprocessor performs macro substitution, file inclusion
and conditional compilation on the source program. Use the `-
s' compiler option to see the program text after
preprocessing.
The preprocessor allows a line to be continued by ending it
with a `\' character. Tokens and strings can be split across
lines.
Preprocessor directives are lines starting with `#'. A line
with only '#' is a null directive and is ignored by the
preprocessor.
Macro substitution
The #define preprocessor directive defines a macro, optional
parameter list and associated substitution string. A macro can
have no parameter list, eg
#define WORD_SIZE 16
then every occurrence of WORD_SIZE in the program text is
replaced with `16'.
The general form of a macro definition allows zero or more
parameters;
#define <name>(parameters) <replacement text>
#define afunc() otherfunc()
#define times(x,y) ((x)*(y))
every call to afunc() is replaced with a call to otherfunc();
when `times' is used, actual arguments are substituted for
macro parameters in the replacement text, eg
times(4,times(5,6))
gives
((4)*(((5)*(6))))
The number of arguments when the macro is used must match the
number of parameters when it is declared.
A macro definition may be split across more than one line by
line continuation with \
#define plus(x,y) \
((x) + (y))
After macro substitution, the line is scanned again so macros
created by the expansion can be recognized and expanded.
The following macros are predefined by the preprocessor;
MIRACLE defined as 1
use for code specific to Miracle C compiler
MSDOS defined as 1
use for code specific to MS-DOS
__FILE__ current source file
__LINE__ line in current source file
__DATE__ today's date, returned as "Mmm dd yyyy"
__TIME__ time of compilation, returned as "hh:mm:ss"
Redefinition of macros (benign or otherwise) is allowed. No
warning or error is given if redefinition occurs.
A macro definition can be cleared using `#undef' directive;
#undef times
File inclusion
The `#include' directive allows C source files to be included
in the program text. When the end of the included file is
reached, compilation continues from the line after the
`#include' directive. Include files may be nested; but they
should including themselves, directly or indirectly, causes
the compiler to crash. Preprocessor include files usually have
a .h suffix (header files). There are two forms of #include
directive;
#include "filename"
includes a file in the current directory,
#include <filename>
includes a file from the system include directory, which is
given by the INCLUDE environment variable. If INCLUDE is not
set, the system include directory defaults to `\cc\include'.
The inclusion-file can be specified by a macro;
#define MYINCLUDES "my.h"
#include MYINCLUDES
A complete pathname can be given in the first form of include;
#include "\cc\graphics\graf.h"
Conditional Compilation
The preprocessor allows compilation of sections of code to be
conditional on the values of expressions, using the directives
`#if' `#ifdef' `#ifndef' `#else' `#endif'. Lines after an
unsuccessful conditional compilation directive are discarded
until the next conditional compilation directive.
A conditionally compiled section of code starts with #if
#ifdef or #ifndef and ends with a matching #endif. It may
contain #else or #elif directives, and other (nested)
conditionally compiled code sections.
`#if expr'
If the C expression `expr' evaluates to 1, lines are processed
until a matching else, elif or endif is found. If it evaluates
to 0, following lines of code are discarded until a matching
else, elif or endif is found. The expression `expr' must be a
constant recognisable to the preprocessor, containing macro,
character and number constant values only.
`#ifdef name' `#ifndef name'
If the macro `name' is defined (ifdef) or undefined (ifndef),
lines are processed until a matching else, elif or endif is
found. Otherwise, following lines of code are discarded until
a matching else, elif or endif is found.
`#else'
The else directive follows an if, ifdef or ifndef, or can
follow #elif directives. Lines of code following #else are
processed only if preceeding lines were not processed.
`#elif'
This is a switch/case construct for conditional compilation;
#if expr1
...
#elif expr2
...
#elif expr3
(etc)
#else
...
#endif
`#endif'
terminates a conditionally compiled code section.
Other Features
The #error directive aborts compilation with an error message;
#ifdef x_once
#error Illegal include recursion
#endif
#define x_once
detects a program's attempt to include itself; implement
"once-only" inclusion of header files similarly.
Stringification converts a token into a string, and is useful
when debugging a program;
#define fatal(tok1,tok2) printf("bad tokens:
%s;%s;",#tok1,#tok2)
Concatenation of adjacent strings is supported by Miracle C
compiler,
so
#define fatal(tok1,tok2) printf("bad tokens: " #tok1 ";"
#tok2)
is allowed.
Token merging using the ## operator creates a single token
from two or more
tokens in a macro definition;
#define line(i) line ## i
then line(1) == line(2)
becomes line1 == line2
Functions
Miracle C supports a both the traditional syntax for function
definition, and a syntax similar to ANSI C for function
prototyping. Functions can be declared and prototyped before
use. The traditional way of introducing functions, eg.
int main(argc,argv) int argc; char **argv;
is supported. Traditionally, functions weren't declared before
use or definition; CC allows functions to be declared and
prototyped before use, zero times or exactly once, or a parse
error results. Note that 'parse errors' occur when the current
symbol is not one of the expected legal symbols, hence a
function redeclaration may generate a parse error.
CC's function declaration syntax is similar to ANSI, eg
void afunc(int one, char *b);
but the ANSI syntax
void afunc(int, char *);
is not supported (yet). The number and type of parameters, and
return value, in a function definition must match exactly that
of the function declaration. Miracle CC supports declaration
of functions with a variable number of parameters, eg
int printf(char *fmt, ...);
but their definition is not supported (yet).
Miracle C has the concept of `function types' which are
type1 x .. x typen x varargs -> typer
for a function taking n parameters of type (type1, ..., typen)
and returning a result of type typer, with a variable number
of arguments if varargs set.
A pointer to a function is assigned a function type, which
should match the type of a function assigned to it; for
example,
void (*fptr)();
int printf(char *fmt, ...);
(fptr=&printf,*fptr)("hello");
will fail with the error message
158: wrong # args in function call
'(fptr=&printf,*fptr)("hello")'
Functions may return signed or unsigned int, char or long
values, but not struct or union. They may return pointers to
any item (including struct), or a user defined type, as long
as that type isn't a struct. Nor can a function return an
array, or another function.
Parameters in a function call are pushed on the stack using
normal C linkage conventions, ie right-to-left, to allow for
functions with a variable number of parameters (eg printf,
first argument specifies number and type of subsequent).
Parameters should be word entities, ie int char and pointer
type.
Functions may be declared as extern, static or register, but
these classes are ignored by the compiler. Miracle C marks
functions defined in the program text as `internal' and others
as `external' and generates object accordingly. Static
functions (visible only within a program) are not supported
(yet!).
Miracle C needs functions with no parameters to be declared by
int afunc();
It does not follow the ANSI convention of declaring such as
function by
int afunc(void);
nor does it follow the traditional C convention of declaring a
function's return type, but omitting a declaration of
parameters, using the above syntax.
Parameters in a definition count as local variables in the
highest level block in the function body, following the ANSI
definition.
Storage classes are not allowed for parameters in a
definition. Functions with return type `void' must not return
a value.
A function declared to have return type `int' need not specify
return type in the function definition, so we allow
int main();
....
main() { ... }
Function prototypes may not be nested;
void (*signal(int sig, void (*func)(int sig)))(int sig);
is illegal, but can circumvent this by
typedef void sig_handler(int sig);
sig_handler *signal(int sig,sig_handler *func);
Autoprototyping of functions is allowed. If a function call is
introduced for a function which has not been declared, a
declaration is automatically generated for the function. The
automatically generated prototype will use the types of
parameters which are given to the function on call, and will
assume a function return type of int. If the desired function
type is different to that which will be implicitly generated
from the function call then an explicit declaration should be
made before the function is called. Function autoprototyping
may be disabled by a compiler flag.
Types
Miracle C supports void, scalars (pointers, signed and
unsigned char int long), enumeration types, floating point
types, pointers to any object, struct/union and multi-
dimensional array types. It also has `function types' (see the
section on functions).
As a small-model 8086 compiler, CC supports 16-bit ints and
pointers, 8 bit char values (expanded to 16 bits when passed
as parameters on the stack), and 32 bit longs. The type
`short' is a synonym for `int'. An object declared by
signed avar;
is a signed integer. Floating point numbers are not
implemented. Long integers are not completely implemented; for
example, adding a long to an int is not allowed, but adding
two longs is (so cast the int to long before adding); and long
values should not be passed as function arguments.
Miracle C maintains a type value for each expression, computed
from the types of its components; a data size is associated
with each type value.Traditional C casts are allowed, but type
checking of assignments etc is not strict.
Array subscripts, and adding an int to a pointer, is scaled up
according to the data size of the type being pointed to, eg if
intptr is int * then intptr+1 (or intptr[1]) will point to
intptr plus 2 in memory.
Arrays of any type except void can be declared and can be
multi-dimensional. Void variables are not allowed.
Enumerations are introduced by an `enum' declaration, such as
enum colour { red, green, blue } mycolour;
enum colour anothercolour.
The tag (here `colour') is optional, and can be used in a
later declaration. Enumerations must start at zero, the
traditional "red=2" syntax is not supported. An enum variable
is int. The enum namespace is separate from others, hence
enum one { one, two } number;
is allowed.
Record (Struct/Union)
Structure (record) type is a collection of named components;
struct structag
{
int x, y[3];
char z;
struct structag *sptr, *tptr;
}
sarray[10], *sptr;
struct structag another, *anotherptrs[5];
Structure tag `structag' identifies this structure type for
other declarations. The struct has components x,y,z and two
pointers to other `structag' structs (so we've declared a tree
structure type). Structure declarations cannot be nested (that
would be infinitely silly). Structure component names live in
a separate namespace for each struct/union type, hence we can
introduce variables with the same name. Struct components
occupy successive memory locations, and the size of a struct
(as given by sizeof) is the sum of the sizes of it's
components.
Struct components are referred to using . and -> operators. In
the example above we declare sarray an array of 10 `structag'
structs and allocate space for it (either static or on the
stack). Direct reference by sarray[i].y[0], indirect reference
via pointer to struct by sptr->z, sptr->tptr->y[i].
A union is declared using the same syntax as struct, but
contains only one of its members at any one time (so the size
of a union is the max of the size of it's components, and all
items are at offset zero in the union).
Nested structs/unions are allowed. Bit fields in structs
aren't implemented.
Struct bitfields are permitted. A single bitfield must not
exceed the capacity of a machine word (16 bits). All the
normal arithmetic operations and assignment are permitted on
bitfields, as are struct bitfield initializers.
Typedef
User defined type synonyms are introduced by the `typedef'
statement;
typedef int *ptr, (*func)(), afun();
ptr wordptr;
afun main;
func funcptr=main;
ptr `ptr to int'
func `ptr to function: void->int'
afun `function: void->int'
Typedef doesn't create a new type, only type synonyms, so type
compatibility/comparison works. Typedef for function can
include function prototypes;
typedef int funci(char *x);
funci funky;
A typedef name may be global, or local to a function. The ANSI
standard allows typedefs to be redeclared in a block, but CC
doesn't (yet).
Type Compatibility
Two expressions are assignment compatible t1=t2 if t1 is an
lvalue, and;
- t1 is (u)long, t2 is (u)int/char, extended to 32 bits
- t1 is (u)int/char, t2 is (u)long, truncated to 16 bits
- t1 and t2 are both (u)long, 32 bit assignment
- t1 is (u)char, 8 bit assignment
else 16 bit assignment (of int, pointer etc)
Bitfields may be signed or unsigned integer quantities and
have the same type compatibility rules as integers.
Some operators eg || && ^ | & * / % require (u)int operands.
Pointers may be subtracted (t1-t2 yields a pointer of type t1)
but not added (addition of pointer is meaningless).
Array types count as ptrs, depth of ptr=dimensionality of
array.
Struct/unions types are compatible if they have the same
struct tag.
Declarations
Variables, functions and user defined data types are
introduced by declaration statements. A declarator gives
identifier name and type;
Scalar int x
Floating point double x;
Pointer int *x; char *y[]; (y is pointer to
array, same as char **y;)
Array int (*x)[4]; (x is pointer to array
of 4 ints)
Function int x(), y(int a,char *b), (*x[])(int a);
To parse a declaration, start from name and work out according
to precedence rules;
int *a[10] a is array of 10 ptrs to int
int (*x[])(int a) x is array of ptrs to functions:
int->int
Global variables are visible throughout the program from the
point of declaration, unless they are hidden by a local
declaration with the same name. Local variables are visible
throughout the block in which they are defined, unless they
are hidden by a declaration at an inner level with the same
name. Globals are extern by default.
Static variables (globals and local statics) have storage
allocated at compile time in static data area to hold a
possible initializer. Local variables are allocated on the
stack at runtime and are local to a block, or are function
parameters.
Parameters to a function are considered as being declared at
the topmost local level in the function, so a local
declaration using a parameter name is an error. Local objects
may not be declared more than once.
All floating point types (float, double and long double) are
treated internally as 8-byte double quantities. Normal
floating point arithmetic is allowed on these quantities. No
support for numeric coprocessing is included in this version.
Initializers are allowed for both global and local objects and
follow traditional syntax. Global initializers are evaluated
at compile-time, a constant is stored in the static data
segment. Therefore, expressions in global initializers can
only contain things which can be evaluated at compile-time,
such as constants and address of objects.
Local initializers are evaluated when a function is called, so
expressions may (even) contain function calls.
Multi-dimensional arrays, structs/unions, arrays of structs
and initializers for them are supported using traditional C
syntax.
A struct tag can be declared before any variables are;
struct S { int a, b; };
struct S fred;
A struct can be declared without tag or variables;
struct { int a,b; };
is allowed but pointless.
Also static struct S { int a,b; }; is allowed but `static' is
meaningless.
A global variable may be declared more than once, but all
declarations must agree on type. A local variable may only be
declared once. Global arrays must have the same dimensions
when redeclared, but the first dimension need not be
specified;
char uu[5][4][3]; char uu[][4][3];
Only one definition may exist for a variable, so a global may
have only one declaration with an initializer. Redeclaration
of functions is not supported.
Type definitions, structs/unions and enumerated types are
supported; see the section on `types'.
Global variables are allocated in a static data area.
Uninitialized globals are set to zero; uninitialized parts of
partly initialized globals are zeroed. Initialized globals
give PUBDEF records for the linker, uninitialized globals give
COMDEF records.
Arrays are allocated at compile-time (static) or run-time
(auto), hence static char p1[]="hello" allocated 6 bytes and
copies "hello" into it, but pointer initialization is to run-
time objects, hence char *p2="hello" allocates 2 bytes for p2
and points it to a static string item.
Statement labels are local to a function body, and the target
of goto statements
here:
...
goto here;
Forward references are allowed if;
- statement label may be used by goto before it is
declared
- an identifier should be visible immediately after
declaration,
eg int fred=sizeof(fred)
but this is not yet implemented
- struct can contain pointer to instance of itself
- function declaration and use before definition
Overloading of identifiers permits an identifier to have
different meanings depending on context;
- macro names are defined and used by the preprocessor
- struct tag names have a separate name space
- enum tag names have a separate name space
- typedef, enum and label names are checked before
variable names.
a compile error results if a variable is declared with
the same
name as a typedef or enum name, or goto-label
- struct component names are specific to a struct/union
type,
so two structs can have components of the same name
External names must be defined at top-level (global
variables). Extern declarations inside functions are not
supported and are treated as declarations of internal
variables. Global variables are defined to the linker and
accessible to other programs.
The storage classes auto and register are ignored by the
compiler. Static variables local to a function are allocated
in the static data area, and should have constant
initializers.
Initializers
The compiler allows initialization of scalars, strings,
struct/unions and arrays. An initializer sets the initial
value of a variable, at compile time for static objects
(globals or local statics) or run time for automatic
variables. If no initializer exists for a global variable,
it's set to zero; if none exists for a local, it's initial
value is unpredictable.
The traditional C syntax for initializers is supported;
int x=4;
struct { int a; char *s; int b; } icky = { 1, "astr", 2
};
enum { red, blue } colour = blue;
Union initializer is for the first component of the union.
Structs and multi-dimensional arrays are initialized
recursively;
int m1[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
The `shape' of an initializer (brace structure) must match
that of the object being initialized. If there aren't enough
initializing items for an array or struct initializer, the
rest of the object is zeroed. If there are too many, a compile
time error results.
If the outermost dimension of an array is unspecified, it's
taken from the initializer;
char a[] = { 'a', 'b', 'c', '\0' }, *b="hello";
Braces can be dropped in the initializer;
int a[2][3]={1,2,3,4,5,6};
the number of items in the initializer must not exceed the
declared object.
Static variable initializer expressions contain only objects
which can be evaluated at compile-time, ie constants and
addresses of static objects. The address of a global variable
cannot be found if it is declared but not defined;
int a, *b=&a;
won't compile, but
int a=2, *b=&a;
is allowed.
Local variable initializer expressions are evaluated at
runtime, when a function is called. They can contain any
expression (even function calls). If goto jumps to a label in
a block, initializers for that block don't run.
Initializers are permitted for floating point variables, and
bitfields in struct (record) types, using the standard C
syntax.
Expressions
An expression consists of operators acting on other
expressions, or base values (variables or constants). An
expression is either an lvalue or an rvalue; an lvalue refers
to an object in memory, eg a variable; an rvalue is a non-
lvalue.
Lvalues can be `variable' e[k] lvalue.name e->name *e
they are used by & ++ -- assignment-operators
Expressions are formed from operators, in precedence order:
, sequential evaluation, yields second operand
= += -= *= /= %=
&= |= ^= >>= <<= assignment
assign (u)long = (u)(int|char)
(u)long = (u)long
(u)(int|char) = (u)long
word/byte lvalue = word/byte rvalue
?: conditional exp1 ? exp2 : exp3
depending on value of exp1, yields exp2 or exp3
type of exp1 must be int
types of exp2 and exp3 must match, and must be byte or
word
|| logical or exp1 || exp2 only evaluate exp2 if exp1
false
&& logical and exp1 || exp2 only evaluate exp2 if exp1
true
| bit or
^ bit xor
& bit and
bit or, xor, and take (u)int operands and yield (u)int
result
== != compare, signed or unsigned
< > signed if at least one operand is signed, both are int or
char
<= >= unsigned if both operands are unsigned int or char,
or pointers
<< >> shift, arithmetic for signed, logical for unsigned
+ - add sub
(u)(char|int), (u)(char|int)
ptr, (u)(char|int) scale up by size of object pointed to
(u)long, (u)long
can subtract two pointers giving an integer
* / % mul div rem
take (u)int operands, yield (u)int result
(type) cast
cannot cast to/from structs/voids
cannot cast between chr and ptr, but can between int and
ptr
if casting long->int then lose high word, int->long high
word zero
* indirection
& address of
- unary minus
! logical not
~ bit not
sizeof sizeof(typename) size of type
sizeof("string") size of constant string + 1
sizeof(array-expr) size of array in bytes
sizeof(expr) sizeof type of expr
++ -- post/pre decr/incr
if pointer, scaled up by size of object pointed to
-> indirect selection by pointer (struct/union member or
bitfield)
. direct selection (struct/union member or bitfield)
f(..) function call, where f is a function designator
f has function type and identifies a function
(expr) brackets
a[k] array subscript; type of k = (u)(int|char)
the result of an array subscript is an lvalue only if a
is a pointer, or we have reached the last
array dimension
for example, if int a[3][2]; then a[1]=4; is
meaningless and is flagged as an error
but this means that
int a[2][3], **x;
x=&a[1];
will not compile. The ANSI solution (`modifiable
lvalues') is not implemented.
number constant
string constant
variable global, local, lstatic
Initializer constant expressions are arithmetic constant
expressions and address constant expressions (not fully
implemented in CC). These can have normal operators & casts,
which are evaluated at compile-time.
Statements
All traditional C statements are implemented, except for
`continue'.
A statement can be one of the following;
expr expression; (discarded)
null null statement (actually a null expression)
label label: stmt;
which can be a goto-label, case-label, or default-label
in switch
goto goto name;
where `name' is a goto-label defined somewhere in
function
block { decl-list; stmt; ... stmt; }
A block starts with zero or more declarations, and
contains zero or more statements. Blocks
may be nested. Names declared within the block hide
declarations of the same name outside
the block, and are visible throughout the block unless
hidden by a declaration at an inner
level.
Goto a label inside a block jumps past local variable
allocation and initialization code; but
the locals are de-allocated at end of block anyway, so
don't jump into a block!
if if (expr) stmt
if (expr) stmt1 else stmt2 NB else belongs to nearest
if
if `expr' evaluates to non-zero, do stmt1 (else do stmt2)
`else' belongs to the nearest `if'
while while (expr) stmt
continue executing `stmt' while `expr' evaluates to non-
zero
do do stmt while(expr)
do `stmt' at least once, then while `expr' evaluates to
non-zero
for for(expr1; expr2; expr3) stmt each of exp1,2,3
optional
expr1= evaluated once at start of loop
expr2= continuation condition, tested before stmt
executed
expr3= iteration expression, evaluated after statement
if expr2 omitted then it defaults to true
eg. for(;;) is an infinite loop
switch switch(expr)
{
case n1: ...;
case n2: ...;
...
default: ...;
}
case labels must be numbers or char literals;
case labels should not be duplicated, at most one default
exists;
when a case or default is chosen, execution continues
until
break or end of block
break terminate smallest enclosing while. do, for, switch
continue continue smallest enclosing while. do, for
return return opt-expr return from function with
optional value
void functions must not return a value
ASSEMBLER PROGRAMMING INTERFACE
Miracle C functions can call, and be called by, assembler
routines.
Function call is by pushing arguments onto the stack in right
to left order,
and call (pushes ip on stack). The called function pushes bp
and allocate
stack space for local variables. Function return is the
reverse; deallocate
local variables, pop bp, return; caller pops arguments.
+-------+
| arg n |
| . |
| . |
| arg 1 |
| ip |
| bp |
| local |
| . |
| . |
| local |
+-------+
At entry to a C function, SP points to the return value of IP
on the stack.
To access word arguments;
push bp
mov bp,sp
mov AX,[bp+2] ; first argument
mov BX,[bp+4] ; second argument
...
pop bp
mov ax,retvalue ; return value
ret
FUNCTION LIBRARY
Startup code
The startup code initializes segment registers, sets argc/argv
parameters
to zero, and calls function `main'. If `main' returns to the
startup routine,
an exit handler is called which closes files and terminates.
Include header files
The following header files are supplied with the compiler in
the 'include' subdirectory.
ctype.h Prototypes for alphanumeric test and conversion
functions.
io.h Prototypes for elementary file functions.
stdio.h Prototypes for higher level file handling and
input/output functions.
string.h Prototypes for memory and string handling
functions.
system.h Prototypes for memory and other functions.
Library Functions
The Miracle C library contains functions for string handling,
file operations, input/output formatting, memory allocation
and other functions.
abs, labs
#include <system.h>
int abs(int i)
long labs(long i)
Produces the absolute (positive or zero) value of its
argument.
abs takes an int argument returning an int, labs takes a
long argument and returns a long.
calloc
#include <system.h>
void *calloc(int nitems, int itemsize)
Allocates a block of memory that ia nitems * itemsize
bytes in size from the heap. The
initial contents of the block is zeroed.
If not enough memory is available to satisfy the request
a null pointer is returned. Since the
compiler uses the small memory model, memory requests
should be made accordingly.
Allocated memory should be freed explicitly when it is no
longer required.
Example
#include <system.h>
#include <stdio.h>
void main()
{
char **buffer;
int nitems=100;
buffer = calloc(nitems,sizeof(char *)); /* allocate
50 pointers to char */
if(buffer==NULL)
{
printf("out of memory\n");
exit(1);
}
printf("calloc allocated %d items of %d at :
%x\n",nitems,sizeof(char *),buffer);
free(buffer);
return;
}
close
#include <io.h>
int close(int fd)
Close the file given by file descriptor handle `fd'
freeing the file descriptor for use by
another file.
close does not write an eof character 26.
Returns 0 if successful, otherwise -1 if failed.
Example
#include <io.h>
#include <stdio.h>
#include <system.h>
void main()
{
int fd;
if((fd = open("tfile",O_RDONLY))<0)
{
printf("failed to open tfile\n");
exit(1);
}
printf("close code %d\n",close(fd));
return;
}
create
#include <io.h>
int create(char *fname)
Create a file with name `fname'.
If successful, returns a file descriptor for the newly
created file. Otherwise returns -1 if
unsuccessful.
Example
#include <io.h>
#include <stdio.h>
void main()
{
int n;
n = create ("tfile");
if (n == -1) {
printf("Cannot create tfile\n");
}
return;
}
exit
#include <system.h>
void exit(int code)
Closes files, flushes all output buffers and terminates
with return code `code'.
Example
#include <io.h>
#include <stdio.h>
#include <system.h>
void main()
{
int n;
n = create ("tfile");
if (n == -1) {
printf("Cannot create tfile\n");
exit(1);
}
exit(0);
}
fclose
#include <stdio.h>
int fclose(FILE *fp)
Close an open file, and flush output buffer for the file.
Returns 0 if successful, EOF if not.
Example
#include <io.h>
#include <stdio.h>
void main()
{
FILE *fp;
if((fp = fopen("tfile","r"))==NULL) {
printf("failed to open file tfile\n");
return;
}
else {
fclose(fp);
printf("closed file tfile\n");
}
return;
}
feof
#include <stdio.h>
int feof(FILE *fp)
Tests end of file condition for file fp. Returns non-zero
if end of file.
After an EOF condition no further reads should be
performed.
Example
#include <io.h>
#include <stdio.h>
void main()
{
FILE *fp;
char buffer[100];
fp = fopen ("tfile", "r");
while ( !feof(fp) )
fgets(buffer, 100, fp);
return;
}
fflush
#include <stdio.h>
int fflush(FILE *fp)
Flush buffer for an output file. If the file is open for
writing the output buffer is wriiten to
disk. If it is open for reading the buffer is cleared and
another read operation is forced
to occur.
Returns 0 is operation successful, otherwise EOF if an
error occurred.
Example
#include <stdio.h>
#include <system.h>
void main()
{
FILE *fp;
if((fp = fopen("tfile","w"))==NULL)
exit(1);
fflush(fp);
}
fgetc
#include <stdio.h>
int fgetc(FILE *fp)
Reads and returns a character from a file. Returns next
character or EOF.
Example
#include <stdio.h>
void main()
{
FILE *fp;
fp = fopen ("tfile", "r");
while ( !feof(fp) )
putchar(fgetc(fp));
return;
}
fgets
#include <stdio.h>
char *fgets(char *buf, int n, FILE *fp)
Get a string (maximum n characters) from file `fp' to
buffer `buf'.
Returns NULL if an error occurred or no characters were
read,
otherwise returns the (null-terminated) string.
Example
#include <io.h>
#include <stdio.h>
void main()
{
FILE *fp;
char buffer[100];
fp = fopen ("tfile", "r");
while ( !feof(fp) )
fgets(buffer, 100, fp);
return;
}
fopen
#include <stdio.h>
FILE *fopen(char *name, char *mode)
Open a file with filename `name' returning a pointer to
FILE.
The following modes are allowed;
`r' open file for reading only
`w' open file for writing
`a' open file for append; position at end of file
`r+' open file for reading and writing
`a+' `w+' create new file for reading and writing
Example
#include <io.h>
#include <stdio.h>
void main()
{
FILE *fp;
if((fp = fopen("tfile","r"))==NULL) {
printf("failed to open file tfile\n");
return;
}
else {
fclose(fp);
printf("closed file tfile\n");
}
return;
}
fprintf
#include <stdio.h>
int fprintf(FILE *fp, char *fmt, ...)
Print formatted values to file. Arguments follow the
format string and are interpreted
according to the format string.
fprintf writes its characters to the file stream fp.
The format string is a sequence of characters with
embedded conversion commands.
Characters at are not part of the conversion command
are output. Conversion commands
are the same as for the printf function.
fprintf returns the number of characters written.
Example
#include <io.h>
#include <stdio.h>
void main()
{
FILE *fp;
char *msg = "number formats are: ";
int n = 42;
fp=fopen("con","w");
fprintf(fp,"%sx: 0%x d: %d o: %o\n",msg,n,n,n,n);
fclose(fp);
return;
}
fputc
#include <stdio.h>
int fputc(int c, FILE *fp)
Write a character c to file fp. Returns the character
written.
Example
#include <io.h>
#include <stdio.h>
void main()
{
FILE *fp;
char *buffer = "this text is written to console";
fp=fopen("con","w");
while(*buffer)
fputc(*buffer++, fp);
fclose(fp);
return;
}
fputs
#include <stdio.h>
int fputs(char *str, FILE *fp)
Write a string str to file stream fp.
Returns non-negative if successful, EOF if error
occurred.
Example
#include <io.h>
#include <stdio.h>
void main()
{
FILE *fp;
char *buffer = "this text is written to console";
fp=fopen("con","w");
fputs(buffer,fp);
fclose(fp);
return;
}
fread
#include <stdio.h>
int fread(void *buf, int sizelem, int n, FILE *fp);
Read `n' items, each of size `sizelem' from file `fp'
into buffer `buf'.
Returns the number of complete elements read.
Example
#include <io.h>
#include <stdio.h>
#include <system.h>
void main()
{
FILE *fp;
char *dest;
int n;
if((fp = fopen("tfile","r")) == NULL) return;
dest = calloc(81,1);
n = fread(dest,1,80,fp);
printf("read %d bytes\n%s",n,dest);
return;
}
free
#include <system.h>
void free(void *ptr)
Free memory block pointed to by `ptr'. The memory block
described by ptr must have been
allocated by calloc, malloc or realloc.
Example
#include <stdio.h>
#include <system.h>
void main()
{
char *p;
if((p = malloc(100)) == NULL) {
printf("out of memory\n");
return; }
free(p);
return;
}
fscanf
#include <stdio.h>
int fscanf(FILE *fp, char *format,...);
Read characters from file `fp' and convert according to
format string `format', storing via
argument pointers which follow `format'.
See the description of`scanf' for input format
specification.
Returns the number of arguments read from input.
Example
#include <stdio.h>
#include <system.h>
void main()
{
FILE *fp;
char fst[10], sec[20];
int n;
fp=fopen("con","r");
fscanf(fp, "%s %s %d",fst,sec,&n);
printf("You typed %s %s %d\n",fst,sec,n);
return;
}
fwrite
#include <stdio.h>
int fwrite(void *buffer,int sizelem, int n,FILE *fp);
Write `n' items, each of size `sizelem' to file `fp' from
buffer `buf'.
Returns the number of complete elements written.
getc
#include <stdio.h>
int getc(char *fp)
Get a single character from file `fp'. Returns the
character, or EOF if input error.
Example
#include <stdio.h>
void main()
{
int n;
FILE *fp;
fp = fopen ("tfile", "r");
while ( !feof(fp) )
putchar(getc(fp));
return;
}
getchar
#include <stdio.h>
int getchar()
Get a single character from stdin. Returns the character,
or EOF if input error.
Example
#include <stdio.h>
void main()
{
int n;
FILE *fp;
while ((n=getchar())!=EOF)
putchar(n);
return;
}
gets
#include <stdio.h>
char *gets(char *buf)
Get string from stdin into buffer `buf'. Reads characters
from file until newline or end-of-
file. The string written to `buf' is null-terminated.
If a string was read into the buffer it's returned, else
returns NULL.
Example
#include <stdio.h>
void main()
{
char buf[80];
gets(buf); puts(buf);
return;
}
is-ctype
#include <ctype.h>
int isxx(char c)
Test a character to see if it's of a specified type.
If it is, a non-zero value is returned; if not, zero is
returned.
isalnum alphanumeric, letter or digit
isalpha alpha, letter
isascii ascii, 0-127
iscntrl control character
isdigit digit
isgraph graphic, printable
islower lowercase letter
isprint printable
ispunct punctuation
isspace space character
isupper uppercase letter
isxdigit hex digit
malloc
#include <system.h>
void *malloc(int n)
Allocate buffer of n bytes from the heap,
Returns address of buffer, or NULL if no memory
available.
Example
#include <stdio.h>
#include <system.h>
void main()
{
char *p;
if((p = malloc(100)) == NULL) {
printf("out of memory\n");
return; }
free(p);
return;
}
memchr
#include <string.h>
void *memchr(void *buf, int c, int count);
Search buffer `buf' for a character `c'. The search stops
when a character `c' is found, or
after `count' bytes.
memcmp
#include <string.h>
int memcmp(void *buf1, void *buf2, int n);
Compare data in buffers `buf1' and `buf2', of size `n'.
Returns = 0 buf1 and buf2 hold identical data
< 0 first differing byte in buf1 < buf2
> 0 first differing byte in buf1 > buf2
memcpy, memmove
#include <string.h>
void *memcpy(void *buf1, void *buf2, int count);
void *memmove(void *buf1, void *buf2, int count);
Copy `count' bytes from buffer `buf2' to buffer `buf1'.
Returns `buf1'.
memset
#include <string.h>
void *memset(void *buf, int val, int n);
Sets contents of buffer `buf' of size `n' to value `val'.
Returns buffer `buf'.
open
#include <io.h>
int open(char *name, int mode)
Open a disk file `name' using read/write mode `mode',
which can be O_RDONLY,
O_WRONLY, O_RDWR
returns file handle for the opened file, or EOF if there
was an error opening the file.
The mode may be 0 read-only O_RDONLY
1 write-only O_WRONLY
2 read-write O_RDWR
Example
#include <io.h>
#include <stdio.h>
void main()
{
int fd;
if (EOF == (fd = open("tfile",O_RDWR))) {
printf ("failed to open tfile");
}
return;
}
printf
#include <stdio.h>
int printf(char *fmt, ...)
Print formatted values to screen. Arguments follow the
format string and are interpreted
according to the format string.
The format string is a sequence of characters with
embedded conversion commands.
Characters that are not part of the conversion command
are output.
printf returns the number of characters written.
The format string can contain text values, or a format
specification for arguments, one per
argument, beginning with a `%' character, the type
matching the argument's type;
% (conversion-flag) (min-field-width) (precision)
(operation)
eg %-20s %12.4d
The conversion flag character can be one of
- left adjust
+ force sign output
0 pad with 0 instead of space
(space) produce sign `-' or space
Minimum field width is the minimum number of characters
output for the field; if fewer
characters are available from the argument, pad
characters (0 or space) are inserted.
Precision is the minimum number of characters printed
from a number, or the maximum
number of characters printed from a string.
Operation specifies the expected argument type and what
will be output; the expected output
type must match the type of the corresponding argument
for output to be meaningful.
`h' short int
`l' long int
`c' character
`d' `i' integer
`o' octal integer
`p' pointer
`x' hexadecimal
`s' string
`u' unsigned
The number of characters written is returned.
Example
#include <io.h>
#include <stdio.h>
void main()
{
char *msg = "number formats are: ";
int n = 42;
printf(fp,"%sx: 0%x d: %d o: %o\n",msg,n,n,n,n);
return;
}
putc
#include <stdio.h>
int putc(int c, FILE *fp)
Write character `c' to file `fp'. Returns `c'.
putchar
#include <stdio.h>
int putchar(int c)
Write character `c' to stdout. Returns `c'.
Example
#include <stdio.h>
void main()
{
int n;
FILE *fp;
fp = fopen ("tfile", "r");
while ( !feof(fp) )
putchar(getc(fp));
return;
}
puts
#include <stdio.h>
int puts(char *str)
Writes string `str' to stdout, then writes a newline
character.
Example
#include <stdio.h>
void main()
{
char buf[80];
gets(buf); puts(buf);
return;
}
read
#include <io.h>
int read(int fd, void *buf, int n)
Read `n' bytes from file given by file descriptor `fd'
into buffer `buf'. Direct DOS read from
file.
Returns -1 error
0 end of file
n number of bytes read
Example
#include <io.h>
#include <stdio.h>
void main()
{
char buf[80];
int fd;
if (EOF == (fd = open("tfile",O_RDWR))) {
printf ("failed to open tfile");
}
read(fd,buf,80); puts(buf);
return;
}
scanf
#include <stdio.h>
int scanf(char *format,...);
Read arguments from stdin. Parse it according to
specification in `format' string, and assign
input to arguments following the format string.
The arguments following `format' must be pointers to
objects where the input is to be stored,
and must match the specification in the format string.
Returns the number of arguments assigned to. If no
arguments were assigned, an EOF is
returned.
The format string may contain;
- space characters; skip whitespace characters in
input.
- any other characters (except %) which should match
input
(match a `%' character by specifying `%%')
- input conversion specification
% (size) conversion-char
If the conversion specification is %*(size) c-char then
the converted input is not
stored and no argument is used.
If `size' is specified then at most `size' characters are
converted. Otherwise
conversion stops when an invalid input character is read.
The conversion character specifies the input object type
and must match the type of
argument pointer;
`h' short int
`l' long int
`c' character
`d' `i' integer
`x' hexadecimal
`s' string
Example
#include <io.h>
#include <stdio.h>
void main()
{
unsigned n;
printf("type a number: ");
scanf("%i",&n);
printf("number %d is %x hex",n,n);
return;
}
sprintf
#include <stdio.h>
int sprintf(char *buf, char *fmt, ...)
Print formatted values to memory. Arguments follow the
format string and are interpreted
according to the format string.
sprintf returns the number of characters written.
See printf for description of format string.
sscanf
#include <stdio.h>
int sscanf(char *buf, char *fmt, ...)
Parse string in buffer `buf' according to specification
in `format' string, and assign input to
arguments following the format string.
The arguments following `format' must be pointers to
objects where the input is to be stored,
and must match the specification in the format string.
Returns the number of arguments assigned to. If no
arguments were assigned, an EOF is
returned.
strcat
#include <string.h>
char *strcat(char *buf1, char *buf2)
Catenate null-terminated string in `buf2' to the string
in buffer `buf1'. Returns `buf1'.
strchr
#include <string.h>
char *strchr(char *str, int c)
Search string `str' for character `c', return first
occurrence.
strcmp
#include <string.h>
int strcmp(char *str1, char *str2)
Compare null-terminated strings `str1' and `str2'. If
they are identical then return 0. If the
first differing character in `str1' is greater than that
in `str2' then return a positive value,
else return a negative value.
strcpy
#include <string.h>
char *strcpy(char *buf1, char *buf2)
Copy null-terminated string in `buf2' to buffer `buf1'.
strdup
#include <string.h>
char *strdup(char *str)
Create copy of null-terminated string `str' in newly
allocated buffer, and return the buffer.
strlen
#include <string.h>
int strlen(char *str)
Return length of null-terminated string `str'.
strlwr
#include <string.h>
char *strlwr(char *str)
Convert string `str' to lowercase and return it.
strncat
#include <string.h>
char *strncat(char *buf1, char *buf2, int n)
Catenate null-terminated string in `buf2' to null-
terminated string in `buf1'. At most `n'
bytes are catenated.
Returns `buf1'.
strncmp
#include <string.h>
int strncmp(char *buf1, char *buf2, int n)
Compare null-terminated strings `str1' and `str2'. The
strings are compared for at most `n'
characters. If they are identical then return 0. If the
first differing character in `str1' is
greater than that in `str2' then return a positive value,
else return a negative value.
strncpy
#include <string.h>
char *strncpy(char *buf1, char *buf2, int n)
Copy null-terminated string in buffer `buf2' to buffer
`buf1'. Exactly `n' characters are
copied. If `buf2' contains fewer than `n' characters,
destination `buf1' is padded with nulls.
strnicmp
#include <string.h>
int strnicmp(char *buf1, char *buf2, int n)
Compares `n' characters of strings `buf1' and `buf2',
ignoring case.
Returns 0 strings equal, ignoring case
< 0 first differing character `buf1' < `buf2'
> 0 first differing character `buf1' > `buf2'
strpbrk
#include <string.h>
char *strpbrk(char *str1, char *str2)
Search string `str1' for a character occurring in string
`str2', return first occurrence.
strrchr
#include <string.h>
char *strrchr(char *buf, int c)
Search null-terminated string in buffer `buf' for
character `c', returning pointer to last
occurrence of `c' in `buf'.
strrev
#include <string.h>
char *strrev(char *str)
Reverses the contents of string `str'. Returns `str'.
strspn
#include <string.h>
int strspn(char *str1, char *str2)
Span string `str1' skipping any characters in string
`str2'.
strupr
#include <string.h>
char *strupr(char *str)
Convert string `str' to uppercase, and return uppercased
string.
tolower, toupper
#include <string.h>
int tolower(int c)
int toupper(int c)
Convert character `c' to lower/upper case.
ungetc
#include <stdio.h>
int ungetc(int c, FILE *fp)
Push character `c' back to input file `fp'. Only one
character may be ungetc'd.
Returns `c' if succeeded, else EOF.
Example
#include <io.h>
#include <stdio.h>
#include <ctype.h>
void main()
{
FILE *fp;
char c;
fp = fopen("tfile","r");
while((c = fgetc(fp)) != EOF)
if(isspace(c))
break;
else
putchar(c);
ungetc(c,fp);
fclose(fp);
return;
}
vsprintf
#include <stdio.h>
int vsprintf(char *buf, char *fmt, char *args)
Prints the arguments pointed to by stack segment pointer
`args' to output buffer `buf'.
See printf for description of format string,
Returns the number of characters written.
write
#include <stdio.h>
int write(int fd, void *buffer, int size);
Write memory buffer of length `size' to file `fd'. This
is a direct DOS write to file.
Returns the number of characters written or -1 if error.
Example
#include <io.h>
#include <stdio.h>
#include <string.h>
void main()
{
int n, fd;
char *buf = "test data to be written";
if((fd = open("tfile",O_WRONLY)) == -1)
{
puts("failed to open file");
return;
}
n = write(fd,buf,strlen(buf));
printf("%u bytes written\n",n);
close(fd);
return;
}